<template>
  <div :class="[$store.state.appPrefix + '-c-video-recorder']">
    <!-- 视频预览区域 -->
    <video
      ref="videoPreview"
      class="video-preview"
      autoplay
      muted
      playsinline
    ></video>
  </div>
</template>

<script>

export default {
  name: 'VideoRecorder',
  props: {
    // 初始质量设置
    initialQuality: {
      type: String,
      default: 'medium',
      validator: value => ['low', 'medium', 'high'].includes(value)
    }
  },
  data() {
    return {
      selectedQuality: this.initialQuality,
      isRecording: false,
      hasPermission: false,
      mediaRecorder: null,
      recordedChunks: [],
      stream: null,
      recordingStartTime: null,
      recordingInfo: null,
      resolvedMimeType: null,
      ffmpeg: null,
      fetchFile: null,
      isTranscoding: false,
      isManualStop: false, // 标识是否是手动停止
      trackEndedHandlers: [], // 保存track的ended事件处理器，便于清理

      // 质量配置
      qualityConfigs: {
        low: {
          width: 640,
          height: 480,
          video: {
            width: { ideal: 640 },
            height: { ideal: 480 },
            frameRate: { ideal: 15, max: 20 } // 降低帧率以减小文件大小
          },
          // 比特率设置（单位：bps）
          videoBitsPerSecond: 500000, // 500kbps - 适合低质量
          audioBitsPerSecond: 64000 // 64kbps - 音频比特率
        },
        medium: {
          width: 1280,
          height: 720,
          video: {
            width: { ideal: 1280 },
            height: { ideal: 720 },
            frameRate: { ideal: 20, max: 25 } // 中等帧率
          },
          // 比特率设置
          videoBitsPerSecond: 1000000, // 1Mbps - 适合中等质量
          audioBitsPerSecond: 96000 // 96kbps
        },
        high: {
          width: 1920,
          height: 1080,
          video: {
            width: { ideal: 1920 },
            height: { ideal: 1080 },
            frameRate: { ideal: 24, max: 30 } // 较高帧率
          },
          // 比特率设置
          videoBitsPerSecond: 2500000, // 2.5Mbps - 适合高质量
          audioBitsPerSecond: 128000 // 128kbps
        }
      }
    }
  },
  computed: {
    currentConfig() {
      return this.qualityConfigs[this.selectedQuality]
    }
  },
  mounted() {
    this.checkPermission()
  },
  beforeDestroy() {
    this.cleanup()
  },
  methods: {
    // 获取getUserMedia方法（兼容各种浏览器）
    getUserMediaCompat() {
      // 优先使用标准的navigator.mediaDevices.getUserMedia
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        return navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
      }

      // 兼容旧版Chrome/Firefox/Safari
      const getUserMedia = navigator.getUserMedia
                       || navigator.webkitGetUserMedia
                       || navigator.mozGetUserMedia
                       || navigator.msGetUserMedia;

      if (getUserMedia) {
        // 转换为Promise形式的API
        return (constraints) => new Promise((resolve, reject) => {
          getUserMedia.call(navigator, constraints, resolve, reject);
        });
      }

      return null;
    },
    // 检查录制权限
    async checkPermission() {
      try {
        // 若已有可用流与预览，直接视为已授权，避免影响当前预览
        if (this.stream && this.$refs.videoPreview?.srcObject === this.stream) {
          this.hasPermission = true
          this.$emit('permission-checked', true)
          return true
        }

        // 使用临时流进行权限探测，不绑定到组件实例与预览
        const getUserMedia = this.getUserMediaCompat()
        if (!getUserMedia) {
          throw new Error('浏览器不支持getUserMedia API')
        }
        const testStream = await getUserMedia({
          video: this.currentConfig.video,
          audio: true
        })

        this.hasPermission = true
        // 立刻释放临时流，避免影响后续真实预览与录制
        testStream.getTracks().forEach(track => track.stop())

        this.$emit('permission-checked', true)
        return true
      } catch (error) {
        this.hasPermission = false
        this.$emit('permission-checked', false)
        console.error('无法获取录制权限:', error)
        return false
      }
    },

    // 仅开启摄像头预览（不开始录制）
    async openCamera(options = { audio: true }) {
      if (!this.hasPermission) {
        await this.checkPermission()
        if (!this.hasPermission) {
          this.$emit('error', '没有录制权限')
          return
        }
      }
      try {
        // 已有流则直接返回
        if (this.stream && this.$refs.videoPreview?.srcObject === this.stream) {
          return true
        }

        const constraints = {
          video: this.currentConfig.video,
          audio: options.audio !== false
        }

        const getUserMedia = this.getUserMediaCompat()
        if (!getUserMedia) {
          throw new Error('浏览器不支持getUserMedia API')
        }
        this.stream = await getUserMedia(constraints)
        this.$refs.videoPreview.srcObject = this.stream
        this.hasPermission = true
        this.$emit('camera-opened')
        return true
      } catch (error) {
        this.hasPermission = false
        this.$emit('error', '开启摄像头失败: ' + error.message)
        console.error('开启摄像头失败:', error)
        return false
      }
    },
    // 开始录制
    async startRecording() {
      if (!this.hasPermission) {
        await this.checkPermission()
        if (!this.hasPermission) {
          this.$emit('error', '没有录制权限')
          return
        }
      }

      try {
        // 如果已经在录制，先停止之前的录制
        if (this.isRecording && this.mediaRecorder) {
          console.warn('检测到已有录制在进行，先停止之前的录制')
          this.isManualStop = true
          this.mediaRecorder.stop()
          // 等待之前的录制完全停止
          await new Promise(resolve => setTimeout(resolve, 100))
        }

        // 清理之前的track监听器
        this.removeTrackListeners()

        // 如果已有流，先清理
        if (this.stream) {
          // 检查流的track是否还在运行
          const activeTracks = this.stream.getTracks().filter(track => track.readyState === 'live')
          if (activeTracks.length > 0) {
            // 如果流还在运行，直接使用现有流
            console.log('使用现有媒体流')
          } else {
            // 如果流已停止，需要重新获取
            this.stream = null
          }
        }

        // 如果没有可用流，获取新的媒体流
        if (!this.stream) {
          const getUserMedia = this.getUserMediaCompat()
          if (!getUserMedia) {
            throw new Error('浏览器不支持getUserMedia API')
          }
          this.stream = await getUserMedia({
            video: this.currentConfig.video,
            audio: true
          })
        }

        this.$refs.videoPreview.srcObject = this.stream

        // 获取配置
        const mimeType = this.getSupportedMimeType()
        this.resolvedMimeType = mimeType
        const config = {}
        if (mimeType) {
          config.mimeType = mimeType
        }

        // 添加比特率配置以压缩文件大小
        // 注意：某些浏览器可能不支持这些配置项，会忽略不支持的选项
        const currentQualityConfig = this.currentConfig
        if (currentQualityConfig.videoBitsPerSecond) {
          config.videoBitsPerSecond = currentQualityConfig.videoBitsPerSecond
        }
        if (currentQualityConfig.audioBitsPerSecond) {
          config.audioBitsPerSecond = currentQualityConfig.audioBitsPerSecond
        }

        console.log('MediaRecorder 配置:', config)

        // 创建 MediaRecorder
        this.mediaRecorder = new MediaRecorder(this.stream, config)

        this.recordedChunks = []
        this.recordingStartTime = Date.now()
        this.isManualStop = false // 重置手动停止标志

        // 监听数据
        this.mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            this.recordedChunks.push(event.data)
          }
        }

        // 监听录制错误
        this.mediaRecorder.onerror = (event) => {
          console.error('MediaRecorder错误:', event.error)
          this.$emit('error', '录制过程中出错: ' + (event.error?.message || '未知错误'))
          // 发生错误时也触发停止
          if (this.isRecording) {
            this.isManualStop = false
            this.onRecordingStopped()
          }
        }

        // 监听录制结束
        this.mediaRecorder.onstop = () => {
          console.log('MediaRecorder onstop 触发', {
            isManualStop: this.isManualStop,
            isRecording: this.isRecording,
            state: this.mediaRecorder?.state
          })

          // 检查是否是意外停止（非手动停止且仍在录制状态）
          if (!this.isManualStop && this.isRecording) {
            console.warn('检测到意外的录制停止！可能是媒体流被中断')
            this.$emit('error', '录制意外停止，可能是摄像头或麦克风被关闭')
          }

          this.onRecordingStopped()
        }

        // 监听媒体流的track结束事件（防止意外停止）
        this.setupTrackListeners()

        // 开始录制
        this.mediaRecorder.start(1000) // 每秒收集一次数据
        this.isRecording = true

        this.$emit('recording-started')
        return true
      } catch (error) {
        this.$emit('error', '开始录制失败: ' + error.message)
        console.error('开始录制失败:', error)
        return false
      }
    },

    // 停止录制
    stopRecording() {
      if (this.mediaRecorder && this.isRecording) {
        // 标记为手动停止
        this.isManualStop = true
        this.isRecording = false // 先设置为false，防止onstop中的重复检查

        try {
          // 检查MediaRecorder的状态
          if (this.mediaRecorder.state === 'recording') {
            this.mediaRecorder.stop()
          } else {
            console.warn('MediaRecorder状态不是recording:', this.mediaRecorder.state)
            // 如果状态异常，直接触发停止处理
            this.onRecordingStopped()
          }
        } catch (error) {
          console.error('停止录制时出错:', error)
          // 即使出错也触发停止处理
          this.onRecordingStopped()
        }

        this.$emit('recording-stopped')
      } else {
        console.warn('尝试停止录制，但录制未在进行中', {
          hasMediaRecorder: !!this.mediaRecorder,
          isRecording: this.isRecording
        })
      }
    },

    // 录制结束处理
    onRecordingStopped() {
      console.log('onRecordingStopped 被调用', {
        isManualStop: this.isManualStop,
        isRecording: this.isRecording,
        chunksLength: this.recordedChunks.length
      })

      // 确保isRecording状态被重置
      this.isRecording = false

      if (this.recordedChunks.length > 0) {
        const mimeType = this.resolvedMimeType || (this.recordedChunks[0] && this.recordedChunks[0].type) || ''
        const blobOptions = mimeType ? { type: mimeType } : undefined
        const blob = new Blob(this.recordedChunks, blobOptions)
        const finalMimeType = blob.type || mimeType
        const url = URL.createObjectURL(blob)

        // 计算录制信息
        const duration = Date.now() - this.recordingStartTime
        const size = this.formatFileSize(blob.size)

        this.recordingInfo = {
          duration: this.formatDuration(duration),
          size: size,
          blob: blob,
          url: url,
          mimeType: finalMimeType
        }

        this.$emit('recording-completed', {
          blob: blob,
          url: url,
          duration: duration,
          size: blob.size,
          mimeType: finalMimeType
        })
      } else {
        console.warn('录制结束但没有录制数据')
      }

      // 清理资源（此时isRecording已经为false，可以安全清理）
      this.cleanup()
    },

    // 清空录制内容
    clearRecording() {
      this.recordedChunks = []
      this.recordingInfo = null
      this.cleanup()
      this.$emit('recording-cleared')
    },

    // 截图
    async takeScreenshot() {
      if (!this.hasPermission) {
        await this.checkPermission()
        if (!this.hasPermission) {
          this.$emit('error', '没有录制权限')
          return null
        }
      }

      try {
        // 确保有视频流
        if (!this.stream) {
          const getUserMedia = this.getUserMediaCompat()
          if (!getUserMedia) {
            throw new Error('浏览器不支持getUserMedia API')
          }
          this.stream = await getUserMedia({
            video: this.currentConfig.video,
            audio: false
          })
          this.$refs.videoPreview.srcObject = this.stream

          // 等待视频流加载完成
          await new Promise((resolve) => {
            this.$refs.videoPreview.onloadedmetadata = () => {
              resolve()
            }
          })
        }

        const video = this.$refs.videoPreview

        // 检查视频是否已加载
        if (video.readyState < 2) {
          console.warn('视频还未加载完成，等待中...')
          await new Promise((resolve) => {
            video.onloadeddata = () => resolve()
          })
        }

        // 检查视频尺寸
        if (video.videoWidth === 0 || video.videoHeight === 0) {
          throw new Error('视频尺寸为0，无法截图')
        }

        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')

        // 使用视频的实际尺寸，而不是配置的尺寸
        canvas.width = video.videoWidth
        canvas.height = video.videoHeight

        console.log('截图参数:', {
          videoWidth: video.videoWidth,
          videoHeight: video.videoHeight,
          canvasWidth: canvas.width,
          canvasHeight: canvas.height,
          videoReadyState: video.readyState
        })

        // 绘制视频帧到canvas
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height)

        // 检查canvas是否有内容
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
        const hasContent = imageData.data.some(pixel => pixel !== 0)

        if (!hasContent) {
          throw new Error('截图内容为空，可能是视频流未正确加载')
        }

        return new Promise((resolve, reject) => {
          canvas.toBlob((blob) => {
            if (blob && blob.size > 0) {
              const url = URL.createObjectURL(blob)
              const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
              const fileName = `screenshot-${timestamp}.png`

              console.log('截图成功:', {
                blobSize: blob.size,
                url: url,
                fileName: fileName,
                mimeType: blob.type
              })

              resolve({
                blob: blob,
                url: url,
                fileName: fileName,
                mimeType: 'image/png'
              })
            } else {
              reject(new Error('截图失败：生成的blob为空或大小为0'))
            }
          }, 'image/png', 1.0) // 使用最高质量
        })

      } catch (error) {
        this.$emit('error', '截图失败: ' + error.message)
        console.error('截图失败:', error)
        return null
      }
    },

    // 下载截图为PNG文件
    async downloadScreenshot() {
      const screenshot = await this.takeScreenshot()
      if (screenshot) {
        const link = document.createElement('a')
        link.href = screenshot.url
        link.download = screenshot.fileName
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)

        // 清理URL对象
        setTimeout(() => {
          URL.revokeObjectURL(screenshot.url)
        }, 1000)

        return screenshot
      }
      return null
    },

    // 质量变更处理
    onQualityChange() {
      this.$emit('quality-changed', this.selectedQuality)
      if (this.isRecording) {
        this.stopRecording()
      }
    },

    // 获取支持的 MIME 类型
    getSupportedMimeType() {
      if (typeof window === 'undefined' || typeof window.MediaRecorder === 'undefined') {
        return ''
      }

      const preferredTypes = [
        'video/mp4;codecs=avc1,mp4a',
        'video/mp4',
        'video/webm;codecs=vp9,opus',
        'video/webm;codecs=vp8,opus',
        'video/webm'
      ]

      for (const type of preferredTypes) {
        try {
          if (MediaRecorder.isTypeSupported(type)) {
            return type
          }
        } catch (e) {
          // 某些浏览器在调用isTypeSupported时可能抛出异常，忽略继续尝试
          console.warn('检测MediaRecorder类型时出现异常:', e)
        }
      }

      return ''
    },

    // 格式化文件大小
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes'
      const k = 1024
      const sizes = ['Bytes', 'KB', 'MB', 'GB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / (k ** i)).toFixed(2)) + ' ' + sizes[i]
    },

    // 格式化时长
    formatDuration(ms) {
      const seconds = Math.floor(ms / 1000)
      const minutes = Math.floor(seconds / 60)
      const hours = Math.floor(minutes / 60)

      if (hours > 0) {
        return `${hours}:${(minutes % 60).toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`
      } else {
        return `${minutes}:${(seconds % 60).toString().padStart(2, '0')}`
      }
    },

    // 设置track监听器（监听track意外结束）
    setupTrackListeners() {
      if (!this.stream) return

      // 清理旧的监听器
      this.removeTrackListeners()

      const tracks = this.stream.getTracks()
      tracks.forEach(track => {
        const endedHandler = () => {
          console.warn('媒体轨道意外结束:', {
            kind: track.kind,
            label: track.label,
            readyState: track.readyState,
            isRecording: this.isRecording
          })

          // 如果正在录制，track结束会导致MediaRecorder自动停止
          if (this.isRecording) {
            console.error('录制中的媒体轨道被意外停止，这会导致录制中断')
            this.$emit('error', `录制中断：${track.kind === 'video' ? '摄像头' : '麦克风'}被关闭`)
          }
        }

        track.addEventListener('ended', endedHandler)
        this.trackEndedHandlers.push({ track, handler: endedHandler })
      })
    },

    // 移除track监听器
    removeTrackListeners() {
      this.trackEndedHandlers.forEach(({ track, handler }) => {
        try {
          track.removeEventListener('ended', handler)
        } catch (e) {
          console.warn('移除track监听器失败:', e)
        }
      })
      this.trackEndedHandlers = []
    },

    // 清理资源
    cleanup() {
      // 移除track监听器
      this.removeTrackListeners()

      // 只有在没有正在录制时才清理流
      if (this.stream && !this.isRecording) {
        this.stream.getTracks().forEach(track => {
          if (track.readyState === 'live') {
            track.stop()
          }
        })
        this.stream = null
      }

      if (this.mediaRecorder) {
        // 如果MediaRecorder还在运行，先停止
        try {
          if (this.mediaRecorder.state === 'recording') {
            this.isManualStop = true
            this.mediaRecorder.stop()
          }
        } catch (e) {
          console.warn('清理MediaRecorder时出错:', e)
        }
        this.mediaRecorder = null
      }

      // 只有在没有正在录制时才清空预览
      if (!this.isRecording) {
        this.$refs.videoPreview.srcObject = null
      }

      // 重置状态
      this.isManualStop = false
      this.resolvedMimeType = null
    }
  }
}
</script>
